package com.hzzftech.watchdog.busi.controller;

import com.hzzftech.watchdog.busi.constants.BusiConstant;
import com.hzzftech.watchdog.busi.domain.KtDispatcher;
import com.hzzftech.watchdog.busi.domain.KtJob;
import com.hzzftech.watchdog.busi.service.IKtDispatcherService;
import com.hzzftech.watchdog.busi.service.IKtJobService;
import com.hzzftech.watchdog.common.annotation.Log;
import com.hzzftech.watchdog.common.constant.Constants;
import com.hzzftech.watchdog.common.core.controller.BaseController;
import com.hzzftech.watchdog.common.core.domain.AjaxResult;
import com.hzzftech.watchdog.common.core.page.TableDataInfo;
import com.hzzftech.watchdog.common.enums.BusinessType;
import com.hzzftech.watchdog.common.exception.job.TaskException;
import com.hzzftech.watchdog.common.utils.StringUtils;
import com.hzzftech.watchdog.common.utils.poi.ExcelUtil;
import com.hzzftech.watchdog.quartz.util.CronUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.ModelMap;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * 调度任务信息操作处理
 *
 * @author kt-watchdog
 */
@Controller
@RequestMapping("/busi/job")
public class KtJobController extends BaseController
{
    private String prefix = "busi/job";

    @Autowired
    private IKtJobService jobService;

    @RequiresPermissions("busi:job:view")
    @GetMapping()
    public String job()
    {
        return prefix + "/job";
    }

    @RequiresPermissions("busi:job:view")
    @GetMapping("/{dpId}")
    public String job1(@PathVariable("dpId") Long dpId, ModelMap mmap)
    {
        mmap.addAttribute("dpId", dpId);
        return prefix + "/job";
    }

    @RequiresPermissions("busi:job:list")
    @PostMapping("/list")
    @ResponseBody
    public TableDataInfo list(KtJob job)
    {
        startPage();
        List<KtJob> list = jobService.selectJobList(job);
        return getDataTable(list);
    }

    @Log(title = "定时任务", businessType = BusinessType.EXPORT)
    @RequiresPermissions("busi:job:export")
    @PostMapping("/export")
    @ResponseBody
    public AjaxResult export(KtJob job)
    {
        List<KtJob> list = jobService.selectJobList(job);
        ExcelUtil<KtJob> util = new ExcelUtil<KtJob>(KtJob.class);
        return util.exportExcel(list, "定时任务");
    }

    @Log(title = "定时任务", businessType = BusinessType.DELETE)
    @RequiresPermissions("busi:job:remove")
    @PostMapping("/remove")
    @ResponseBody
    public AjaxResult remove(String ids) throws SchedulerException
    {
        jobService.deleteJobByIds(ids);
        return success();
    }

    @RequiresPermissions("busi:job:detail")
    @GetMapping("/detail/{jobId}")
    public String detail(@PathVariable("jobId") Long jobId, ModelMap mmap)
    {
        mmap.put("name", "job");
        mmap.put("job", jobService.selectJobById(jobId));
        return prefix + "/detail";
    }

    /**
     * 任务调度状态修改
     */
    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
    @RequiresPermissions("busi:job:changeStatus")
    @PostMapping("/changeStatus")
    @ResponseBody
    public AjaxResult changeStatus(KtJob job) throws SchedulerException
    {
        KtJob newJob = jobService.selectJobById(job.getJobId());
        // 如果要启用这个定时任务，检查对应的调度是否被禁用
        if (newJob.getStatus().equals(BusiConstant.STATUS_YES)) {
            KtDispatcher dispatcher = dispatcherService.selectKtDispatcherById(newJob.getDpId());
            if (dispatcher.getStatus().equals(BusiConstant.STATUS_NO)) {
                return AjaxResult.error("定时任务的调度为禁用状态，请先启用调度！");
            }
        }
        newJob.setStatus(job.getStatus());
        return toAjax(jobService.changeStatus(newJob));
    }

    /**
     * 任务调度立即执行一次
     */
    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
    @RequiresPermissions("busi:job:changeStatus")
    @PostMapping("/run")
    @ResponseBody
    public AjaxResult run(KtJob job) throws SchedulerException
    {
        jobService.run(job);
        return success();
    }

    @Autowired
    private IKtDispatcherService dispatcherService;

    /**
     * 新增调度
     */
    @GetMapping("/add")
    public String add(ModelMap mmap)
    {
        List<KtDispatcher> list = dispatcherService.selectAllEnable();
        mmap.addAttribute("disps", list);
        mmap.addAttribute("type", "1");
        return prefix + "/add";
    }


    /**
     * 新增调度
     */
    @GetMapping("/add/{dpId}")
    public String add2(@PathVariable("dpId") Long dpId, ModelMap mmap)
    {
        mmap.addAttribute("type", "2");
        mmap.addAttribute("disp", dispatcherService.selectKtDispatcherById(dpId));
        return prefix + "/add";
    }

    /**
     * 新增保存调度
     */
    @Log(title = "定时任务", businessType = BusinessType.INSERT)
    @RequiresPermissions("busi:job:add")
    @PostMapping("/add")
    @ResponseBody
    @Transactional
    public AjaxResult addSave(@Validated KtJob job) throws SchedulerException, TaskException
    {
        if (!CronUtils.isValid(job.getCronExpression()))
        {
            return error("新增任务'" + job.getJobName() + "'失败，Cron表达式不正确");
        }
        else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI))
        {
            return error("新增任务'" + job.getJobName() + "'失败，目标字符串不允许'rmi://'调用");
        }
        else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_LDAP))
        {
            return error("新增任务'" + job.getJobName() + "'失败，目标字符串不允许'ldap://'调用");
        }
        else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
        {
            return error("新增任务'" + job.getJobName() + "'失败，目标字符串不允许'http(s)//'调用");
        }
        else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR))
        {
            return error("新增任务'" + job.getJobName() + "'失败，目标字符串存在违规");
        }
        job.setCreateBy(getLoginName());


        int count = 0;

        String dpIds = job.getDpIds();
        for (String dpIdStr : dpIds.split(",")) {
            long dpId = Long.parseLong(dpIdStr);
            KtDispatcher ktDispatcher = dispatcherService.selectKtDispatcherById(dpId);
            if (null == ktDispatcher) {
                return AjaxResult.error("没有这个调度，id="+job.getInvokeTarget());
            }
            String launchJobStr = "KtDispatcherServiceImpl.";
            if (BusiConstant.REPOSITORY_TYPE_GIT.equals(ktDispatcher.getDpRepoType())) {
                launchJobStr += "launchFJob('"+dpId+"', "+nextJobId()+")";
            } else if (BusiConstant.REPOSITORY_TYPE_FILE.equals(ktDispatcher.getDpRepoType())){
                launchJobStr += "launchFJob('"+dpId+"', "+nextJobId()+")";
            } else if (BusiConstant.REPOSITORY_TYPE_DB.equals(ktDispatcher.getDpRepoType())) {
                launchJobStr += "launchFJob('"+dpId+"', "+nextJobId()+")";
            } else {
                return AjaxResult.error("未找到仓库类型");
            }
            job.setInvokeTarget(launchJobStr);
            job.setJobId(null);
            job.setDpId(dpId);
            count += jobService.insertJob(job);
        }

        return toAjax(count);
    }

    private Long nextJobId() {
        return jobService.nextJobId();
    }

    /**
     * 修改调度
     */
    @RequiresPermissions("busi:job:edit")
    @GetMapping("/edit/{jobId}")
    public String edit(@PathVariable("jobId") Long jobId, ModelMap mmap)
    {
        mmap.put("job", jobService.selectJobById(jobId));
        List<KtDispatcher> list = dispatcherService.selectAllEnable();
        mmap.addAttribute("disps", list);
        return prefix + "/edit";
    }

    /**
     * 修改保存调度
     */
    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
    @RequiresPermissions("busi:job:edit")
    @PostMapping("/edit")
    @ResponseBody
    public AjaxResult editSave(@Validated KtJob job) throws SchedulerException, TaskException
    {
        if (!CronUtils.isValid(job.getCronExpression()))
        {
            return error("修改任务'" + job.getJobName() + "'失败，Cron表达式不正确");
        }
        else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI))
        {
            return error("修改任务'" + job.getJobName() + "'失败，目标字符串不允许'rmi://'调用");
        }
        else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_LDAP))
        {
            return error("修改任务'" + job.getJobName() + "'失败，目标字符串不允许'ldap://'调用");
        }
        else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
        {
            return error("修改任务'" + job.getJobName() + "'失败，目标字符串不允许'http(s)//'调用");
        }
        else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR))
        {
            return error("修改任务'" + job.getJobName() + "'失败，目标字符串存在违规");
        }

        KtDispatcher ktDispatcher = dispatcherService.selectKtDispatcherById(job.getDpId());
        if (null == ktDispatcher) {
            return AjaxResult.error("没有这个调度，id="+job.getInvokeTarget());
        }
        String launchJobStr = "KtDispatcherServiceImpl.";
        if (BusiConstant.REPOSITORY_TYPE_GIT.equals(ktDispatcher.getDpRepoType())) {
            launchJobStr += "launchFJob('"+job.getDpId()+"', "+nextJobId()+")";
        } else if (BusiConstant.REPOSITORY_TYPE_FILE.equals(ktDispatcher.getDpRepoType())){
            launchJobStr += "launchFJob('"+job.getDpId()+"', "+nextJobId()+")";
        } else if (BusiConstant.REPOSITORY_TYPE_DB.equals(ktDispatcher.getDpRepoType())) {
            launchJobStr += "launchFJob('"+job.getDpId()+"', "+nextJobId()+")";
        } else {
            return AjaxResult.error("未找到仓库类型");
        }
        job.setInvokeTarget(launchJobStr);

        return toAjax(jobService.updateJob(job));
    }

    /**
     * 校验cron表达式是否有效
     */
    @PostMapping("/checkCronExpressionIsValid")
    @ResponseBody
    public boolean checkCronExpressionIsValid(KtJob job)
    {
        return jobService.checkCronExpressionIsValid(job.getCronExpression());
    }

    /**
     * Cron表达式在线生成
     */
    @GetMapping("/cron")
    public String cron()
    {
        return prefix + "/cron";
    }

    /**
     * 查询cron表达式近5次的执行时间
     */
    @GetMapping("/queryCronExpression")
    @ResponseBody
    public AjaxResult queryCronExpression(@RequestParam(value = "cronExpression", required = false) String cronExpression)
    {
        if (jobService.checkCronExpressionIsValid(cronExpression))
        {
            List<String> dateList = CronUtils.getRecentTriggerTime(cronExpression);
            return success(dateList);
        }
        else
        {
            return error("表达式无效");
        }
    }
}
